<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>嵌入式小工具集</title>
    <style>
        body {
            font-family: 'Segoe UI', '微软雅黑', Arial, sans-serif;
            background: linear-gradient(120deg, #e0f7fa 0%, #fff 100%);
            min-height: 100vh;
            margin: 0;
            padding: 0;
        }
        .converter {
            background: #fff;
            max-width: 700px;
            margin: 40px auto 24px auto;
            border-radius: 12px;
            box-shadow: 0 4px 24px rgba(0,0,0,0.08);
            padding: 32px 28px 28px 28px;
        }
        h1 {
            text-align: center;
            color: #009688;
            margin-bottom: 28px;
            font-size: 2rem;
        }
        .tabs {
            margin-bottom: 18px;
            display: flex;
            gap: 10px;
            border: 1px solid #e0e0e0;
            background-color: #f8f9fa;
            border-radius: 6px;
            flex-wrap: wrap;
            overflow-x: auto;
        }
        .tab-btn {
            background-color: inherit;
            border: none;
            outline: none;
            cursor: pointer;
            padding: 12px 12px;
            transition: 0.3s;
            font-size: 15px;
            color: #666;
            border-radius: 6px 6px 0 0;
            white-space: nowrap;
            min-width: 110px;
        }
        .tab-btn:hover {
            background-color: #e0f2f1;
            color: #26a69a;
        }
        .tab-btn.active {
            background-color: #b2dfdb;
            color: #004d40;
            font-weight: 500;
        }
        .tab-content {
            display: none;
            padding: 20px 10px 10px 10px;
            border: 1px solid #e0e0e0;
            border-top: none;
            background-color: white;
            border-radius: 0 0 6px 6px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
        }
        .tab-content.active {
            display: block;
        }
        .input-group {
            margin-bottom: 18px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
            color: #444;
        }
        .row {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        input[type="text"], textarea {
            width: 100%;
            padding: 8px 12px;
            border: 1px solid #b2dfdb;
            border-radius: 4px;
            box-sizing: border-box;
            font-size: 15px;
            transition: border-color 0.2s;
        }
        input[type="text"]:focus, textarea:focus {
            border-color: #009688;
            outline: none;
        }
        textarea {
            resize: vertical;
            min-height: 60px;
        }
        button, .copy-btn {
            background-color: #4db6ac;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 8px 14px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.2s;
        }
        button:hover, .copy-btn:hover {
            background-color: #26a69a;
        }
        .copy-btn {
            margin-left: 4px;
            padding: 6px 10px;
            font-size: 13px;
        }
        .error {
            color: #e53935;
            font-size: 0.98em;
            margin-top: 4px;
            min-height: 20px;
        }
        .result {
            background: #f1f8e9;
            border: 1px solid #c5e1a5;
            border-radius: 5px;
            padding: 10px 10px;
            margin-top: 8px;
            font-size: 1.05rem;
            word-break: break-all;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
        .switch {
            display: flex;
            align-items: center;
            gap: 6px;
            margin-bottom: 12px;
        }
        .switch input[type="checkbox"] {
            accent-color: #009688;
        }
        .footer {
            text-align: center;
            color: #aaa;
            font-size: 0.95em;
            margin-top: 30px;
        }
        .hash-table {
            width: 100%;
            max-width: 700px;
            margin: 0 auto 10px auto;
            font-size: 15px;
            table-layout: fixed;
        }
        .hash-table th {
            padding: 8px;
            background: #f8f9fa;
            border-bottom: 1px solid #e0e0e0;
            text-align: left;
        }
        .hash-table td {
            padding: 8px;
            border-bottom: 1px solid #e0e0e0;
        }
        .hash-output {
            width: 100%;
            border: none;
            background: #f1f8e9;
            padding: 6px;
            border-radius: 4px;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <div class="converter">
        <h1>嵌入式小工具集</h1>
        <div class="tabs" style="margin-bottom:18px; flex-wrap: wrap;">
            <button class="tab-btn active" onclick="showTab('float-hex', this)">🔄 Hex-Float转换</button>
            <button class="tab-btn" onclick="showTab('ascii-hex', this)">🔡 ASCII-Hex转换</button>
            <button class="tab-btn" onclick="showTab('timestamp-date', this)">⏰ 时间戳-日期</button>
            <button class="tab-btn" onclick="showTab('json-builder', this)">🧩 JSON组装</button>
            <button class="tab-btn" onclick="showTab('hash-tool', this)">🔑 哈希/校验</button>
            <button class="tab-btn" onclick="showTab('hex-c-array', this)">🧮 Hex-C数组</button>
            <button class="tab-btn" onclick="showTab('hex-byte-count', this)">📏 Hex字节计数</button>
            <button class="tab-btn" onclick="showTab('c-encode-demo', this)">💻 C编码示例</button>
            <button class="tab-btn" onclick="showTab('resource-analyze', this)">📊 资源占用分析</button>
            <button class="tab-btn" onclick="showTab('bit-analyze', this); analyzeBits('');">🧩 Bit位分析</button>
            <button class="tab-btn" onclick="showTab('hex-batch-addsub', this)">➕➖ Hex批量加减</button>
            <button class="tab-btn" onclick="showTab('hex-endian-switch', this)">🔃 Hex字节序切换</button>
            <button class="tab-btn" onclick="showTab('cn-encode', this)">🌐 中文编码转换</button>
            <button class="tab-btn" onclick="showTab('data-fit', this)">📈 数据曲线拟合</button>
            <!-- 以后新增工具直接在这里加按钮即可 -->
        </div>
        <!-- Hex-Float Tab -->
        <div class="tab-content active" id="float-hex">
            <div class="switch">
                <input type="checkbox" id="endianSwitch" checked>
                <label for="endianSwitch" style="margin:0;cursor:pointer;">大端模式（Big Endian）</label>
            </div>
            <div class="input-group">
                <label for="hexInput">Hex输入:</label>
                <div class="row">
                    <input type="text" id="hexInput" placeholder="输入8位Hex值">
                    <button onclick="hexToFloat()">转换为Float</button>
                    <button class="copy-btn" onclick="copyResult('hexInput')">复制</button>
                </div>
                <div class="error" id="hexError"></div>
            </div>
            <div class="input-group">
                <label for="floatInput">Float输入:</label>
                <div class="row">
                    <input type="text" id="floatInput" placeholder="输入Float值">
                    <button onclick="floatToHex()">转换为Hex</button>
                    <button class="copy-btn" onclick="copyResult('floatInput')">复制</button>
                </div>
                <div class="error" id="floatError"></div>
            </div>
        </div>
        <!-- ASCII-Hex Tab -->
        <div class="tab-content" id="ascii-hex">
            <div class="input-group">
                <label for="asciiInput">ASCII输入:</label>
                <div class="row">
                    <input type="text" id="asciiInput" placeholder="输入ASCII字符串">
                    <button onclick="asciiToHex()">转换为Hex</button>
                    <button class="copy-btn" onclick="copyResult('asciiInput')">复制ASCII</button>
                </div>
            </div>
            <div class="input-group">
                <label for="hexAsciiInput">Hex输入:</label>
                <div class="row">
                    <input type="text" id="hexAsciiInput" placeholder="输入Hex值（偶数位）">
                    <button onclick="hexToAscii()">转换为ASCII</button>
                    <button class="copy-btn" onclick="copyResult('hexAsciiInput')">复制Hex</button>
                </div>
            </div>
        </div>
        <!-- JSON Builder Tab -->
        <div class="tab-content" id="json-builder">
            <div class="input-group">
                <label>键: <span style="color:#aaa;font-weight:normal;">（如 name）</span></label>
                <input type="text" id="jsonKey" placeholder="输入键名">
            </div>
            <div class="input-group">
                <label>目标层级路径: <span style="color:#aaa;font-weight:normal;">（如 user.info 或 products[0].features）</span></label>
                <input type="text" id="jsonPath" placeholder="示例：user.info 或 products[0].features" oninput="showPathHint()">
                <div id="jsonPathHint" style="color:#26a69a;font-size:0.95em;margin-top:2px;"></div>
            </div>
            <div class="input-group">
                <label>值: <span style="color:#aaa;font-weight:normal;">（如 "张三"、123、true、[1,2,3]、{"a":1}）</span></label>
                <div class="row">
                    <input type="text" id="jsonValue" placeholder="输入值" style="flex: 1;">
                    <select id="jsonValueType">
                        <option value="string">字符串</option>
                        <option value="number">数字</option>
                        <option value="boolean">布尔值</option>
                        <option value="null">空值</option>
                        <option value="array">数组</option>
                        <option value="object">对象</option>
                    </select>
                    <button onclick="addJsonPair()">添加键值对</button>
                </div>
                <div style="color:#aaa;font-size:0.95em;margin-top:2px;">类型自动转换，数组/对象请用英文逗号或冒号分隔</div>
            </div>
            <div class="input-group" style="display:flex;align-items:center;gap:8px;">
                <button onclick="undoJson()">撤销上一步</button>
                <button onclick="previewJson()">预览/校验</button>
                <span id="jsonOpMsg" style="color:#26a69a;font-size:0.98em;"></span>
            </div>
            <div class="input-group">
                <label>JSON结果:</label>
                <div class="row">
                    <textarea id="jsonResult" placeholder="组装的JSON对象" rows="7" spellcheck="false"></textarea>
                    <button class="copy-btn" onclick="copyResult('jsonResult')">复制</button>
                    <button onclick="clearJson()">清空</button>
                </div>
            </div>
            <!-- 新增：JSON字符串数组输出 -->
            <div class="input-group">
                <label>JSON字符串数组:</label>
                <div class="row">
                    <textarea id="jsonStringArrayResult" placeholder="所有叶子节点的JSON字符串数组" rows="3" readonly style="background:#f8f9fa;"></textarea>
                    <button class="copy-btn" onclick="copyResult('jsonStringArrayResult')">复制</button>
                </div>
            </div>
        </div>
        <!-- Timestamp-Date Tab -->
        <div class="tab-content" id="timestamp-date">
            <div class="input-group">
                <label for="timestampInput">时间戳输入:</label>
                <div class="row">
                    <input type="text" id="timestampInput" placeholder="输入时间戳">
                    <button onclick="timestampToDate()">转换为日期</button>
                    <button class="copy-btn" onclick="copyResult('timestampInput')">复制</button>
                </div>
                <div style="margin-top:8px;">
                    <label><input type="radio" name="timestampMode" value="seconds" checked onchange="updateDateFormatLabel()"> 秒级</label>
                    <label style="margin-left:16px;"><input type="radio" name="timestampMode" value="milliseconds" onchange="updateDateFormatLabel()"> 毫秒级</label>
                </div>
            </div>
            <div class="input-group">
                <label for="dateInput" id="dateInputLabel">日期输入（YYYY-MM-DD HH:mm:ss）:</label>
                <div class="row">
                    <input type="text" id="dateInput" placeholder="输入日期">
                    <button onclick="dateToTimestamp()">转换为时间戳</button>
                    <button class="copy-btn" onclick="copyResult('dateInput')">复制</button>
                </div>
            </div>
        </div>
        <!-- Hash Tab -->
        <div id="hash-tool" class="tab-content">
            <div style="margin-bottom:8px;">
                <label><input type="radio" name="hashInputType" value="text" checked onchange="document.getElementById('hashInputArea').style.display='block';document.getElementById('hashFileArea').style.display='none';calcUnifiedHash();"> 文本</label>
                <label style="margin-left:16px;"><input type="radio" name="hashInputType" value="file" onchange="document.getElementById('hashInputArea').style.display='none';document.getElementById('hashFileArea').style.display='block';calcUnifiedHash();"> 文件</label>
            </div>
            <div id="hashInputArea">
                <textarea id="hashInput" rows="3" style="width:100%;resize:vertical;" placeholder="请输入内容（如 Hex: 01 02 0A FF 或 字符串: 01020304）" oninput="calcUnifiedHash()"></textarea>
                <div style="color:#888;font-size:13px;margin:2px 0 0 2px;">
                  <span style="margin-right:12px;">输入内容解析方式：</span>
                  <label><input type="radio" name="hashParseMode" value="hex" checked onchange="calcUnifiedHash()"> 按Hex字节流</label>
                  <label style="margin-left:16px;"><input type="radio" name="hashParseMode" value="str" onchange="calcUnifiedHash()"> 按字符串</label>
                </div>
                <div style="color:#aaa;font-size:12px;margin:2px 0 0 2px;">“按Hex字节流”将每2位Hex转为1字节（如01020304→4字节），“按字符串”直接对文本内容做哈希（如01020304→8字节ASCII）。</div>
            </div>
            <div id="hashFileArea" style="display:none;">
                <input type="file" id="hashFileInput" onchange="calcUnifiedHash()">
                <span id="hashFileName" style="margin-left:8px;color:#888;"></span>
                <div id="hashFileInfo" style="margin-top:4px;color:#666;font-size:12px;"></div>
            </div>
            <div style="margin:10px 0 6px 0;color:#666;font-size:13px;">
                支持常用哈希算法（MD5、SHA1、SHA256）和CRC校验（CRC16-CCITT/XModem、CRC32）。<br>
                <b>哈希</b>用于数据完整性校验、数字签名等，<b>CRC</b>常用于数据传输/存储的错误检测。<br>
                <b>新增大文件支持：</b>自动识别文件大小，大文件(>50MB)使用分块处理，避免内存溢出，支持GB级文件计算。<br>
                选择文本或文件输入，所有结果自动计算，可一键复制。
            </div>
            <table class="hash-table" style="width:100%;max-width:700px;margin:0 auto 10px auto;font-size:15px;table-layout:fixed;">
                <tr>
                  <th style="width:110px;">算法</th>
                  <th>结果</th>
                  <th style="width:60px;">操作</th>
                </tr>
                <tr>
                  <td>MD5</td>
                  <td><input id="md5UnifiedOutput" class="hash-output" readonly></td>
                  <td><button onclick="copyResult('md5-unified')">复制</button></td>
                </tr>
                <tr>
                  <td>SHA1</td>
                  <td><input id="sha1UnifiedOutput" class="hash-output" readonly></td>
                  <td><button onclick="copyResult('sha1-unified')">复制</button></td>
                </tr>
                <tr>
                  <td>SHA256</td>
                  <td><input id="sha256UnifiedOutput" class="hash-output" readonly></td>
                  <td><button onclick="copyResult('sha256-unified')">复制</button></td>
                </tr>
                <tr>
                  <td>CRC16-CCITT (XModem)</td>
                  <td><input id="crc16UnifiedOutput" class="hash-output" readonly></td>
                  <td><button onclick="copyResult('crc16-unified')">复制</button></td>
                </tr>
                <tr>
                  <td>CRC32</td>
                  <td><input id="crc32UnifiedOutput" class="hash-output" readonly></td>
                  <td><button onclick="copyResult('crc32-unified')">复制</button></td>
                </tr>
                <tr>
                  <td>和校验（加和）</td>
                  <td><input id="sumUnifiedOutput" class="hash-output" readonly></td>
                  <td><button onclick="copyResult('sum-unified')">复制</button></td>
                </tr>
            </table>
            <div id="hashCopyMsg" style="color:#1a7f37;font-size:13px;"></div>
        </div>
        <!-- Hex-C数组 Tab -->
        <div class="tab-content" id="hex-c-array">
            <div class="input-group">
                <label for="hexCArrayInput">Hex字符串转C数组:</label>
                <div class="row">
                    <input type="text" id="hexCArrayInput" placeholder="如 0102030A0BFF">
                    <button onclick="hexToCArray()">转C数组</button>
                    <button class="copy-btn" onclick="copyResult('hexCArrayInput')">复制</button>
                </div>
                <div class="error" id="hexCArrayError"></div>
            </div>
            <div class="input-group">
                <label for="cArrayHexInput">C数组转Hex字符串:</label>
                <div class="row">
                    <input type="text" id="cArrayHexInput" placeholder="如 0x01,0x02,0x03,0x0A,0x0B,0xFF">
                    <button onclick="cArrayToHex()">转Hex</button>
                    <button class="copy-btn" onclick="copyResult('cArrayHexInput')">复制</button>
                </div>
                <div class="error" id="cArrayHexError"></div>
            </div>
        </div>
        <!-- Byte Count Tab -->
        <div class="tab-content" id="hex-byte-count">
            <div class="input-group">
                <label for="hexByteCountInput">Hex字符串:</label>
                <div class="row">
                    <input type="text" id="hexByteCountInput" placeholder="如 0102030A0BFF">
                    <button onclick="countHexBytes()">计算字节数</button>
                    <button class="copy-btn" onclick="copyResult('hexByteCountInput')">复制空格Hex</button>
                </div>
                <div class="error" id="hexByteCountError"></div>
            </div>
            <div class="input-group">
                <label>字节数:</label>
                <input type="text" id="hexByteCountResult" readonly>
            </div>
        </div>
        <!-- C语言编码转换示例 Tab -->
        <div class="tab-content" id="c-encode-demo">
            <div class="input-group">
                <label style="font-weight:bold;">常用编码转换C语言实现：</label>
                <div style="color:#888;font-size:14px;margin-bottom:8px;">可直接复制到工程中使用，适合嵌入式/协议开发。</div>
                <div style="margin-bottom:12px;">
                    <select id="cEncodeSelect" onchange="showCEncodeCode()">
                        <option value="hex2bcd">Hex转BCD</option>
                        <option value="bcd2hex">BCD转Hex</option>
                        <option value="hex2str">Hex转字符串</option>
                        <option value="str2hex">字符串转Hex</option>
                        <option value="bcd2str">BCD转字符串</option>
                        <option value="str2bcd">字符串转BCD</option>
                        <option value="itoa">int转字符串</option>
                        <option value="atoi">字符串转int</option>
                        <option value="ftoa">float转字符串</option>
                        <option value="atof">字符串转float</option>
                        <option value="hexstr2int">16进制字符串转int</option>
                        <option value="int2hexstr">int转16进制字符串</option>
                        <option value="swap16">16位大小端转换</option>
                        <option value="swap32">32位大小端转换</option>
                        <option value="bytes2u16">字节数组转uint16</option>
                        <option value="bytes2u32">字节数组转uint32</option>
                        <option value="u16tobytes">uint16转字节数组</option>
                        <option value="u32tobytes">uint32转字节数组</option>
                    </select>
                    <button class="copy-btn" onclick="copyResult('cEncodeCodeBox')">复制代码</button>
                </div>
                <textarea id="cEncodeCodeBox" rows="12" readonly style="width:100%;font-family:monospace;background:#f8f9fa;"></textarea>
            </div>
        </div>
        <!-- Resource Analyze Tab -->
        <div class="tab-content" id="resource-analyze">
            <div class="input-group">
                <label for="mapFileInput">上传map文件：</label>
                <input type="file" id="mapFileInput" accept=".map" onchange="analyzeMapFile()">
                <span id="mapFileName" style="margin-left:8px;color:#888;"></span>
                <button type="button" onclick="clearMapFile()" style="margin-left:8px;">删除</button>
            </div>
            <div style="color:#888;font-size:12px;margin-bottom:6px;">仅需上传map文件即可分析链接后资源分布，支持GCC/ARMCC/IAR等主流格式，统计各Section（如.text/.data/.bss等）资源占用。</div>
            <div class="input-group">
                <label>分析结果：</label>
                <div class="row">
                    <textarea id="resourceAnalyzeResult" rows="8" readonly style="background:#f8f9fa;"></textarea>
                    <button class="copy-btn" onclick="copyResult('resourceAnalyzeResult')">复制</button>
                </div>
            </div>
            <div id="resourceAnalyzeTableWrap"></div>
            <div id="resourceAnalyzeMsg" style="color:#1a7f37;font-size:13px;"></div>
            <div style="color:#aaa;font-size:12px;margin-top:6px;">支持GCC/ARMCC/IAR等常见map文件格式，自动识别Section/对象/大小，统计各段资源占用。</div>
        </div>
        <!-- Bit Analyzer Tab -->
        <div class="tab-content" id="bit-analyze">
            <div class="input-group">
                <label for="bitInput">输入数值：</label>
                <div class="row">
                    <input type="text" id="bitInput" placeholder="如 0x1F, 31, 0b11111" style="width:340px;max-width:100%;font-size:15px;">
                    <button onclick="analyzeBits()">分析</button>
                    <button onclick="clearBitAnalyze()">清空</button>
                </div>
                <div style="color:#888;font-size:12px;margin-top:2px;">支持十进制、十六进制(0x前缀)、二进制(0b前缀)输入，最大32位。</div>
            </div>
            <div class="input-group">
                <label>Bit位分布与编辑：</label>
                <div id="bitAnalyzeResult" style="overflow-x:auto;max-width:100%;"></div>
            </div>
            <div class="input-group">
                <label>结果：</label>
                <input type="text" id="bitEditValue" readonly style="width:420px;max-width:100%;font-size:14px;">
                <button class="copy-btn" onclick="copyResult('bitEditValue')">复制</button>
            </div>
        </div>
        <!-- Hex批量加减 Tab -->
        <div class="tab-content" id="hex-batch-addsub">
            <div class="input-group">
                <label for="hexBatchInput">Hex输入（支持空格、逗号、换行分隔）:</label>
                <textarea id="hexBatchInput" rows="4" placeholder="如 0A 1B 2C 或 0A,1B,2C 或每行一个"></textarea>
            </div>
            <div class="input-group row" style="align-items:center;gap:12px;">
                <label style="margin:0;">操作:</label>
                <select id="hexBatchOp">
                    <option value="add">加</option>
                    <option value="sub">减</option>
                </select>
                <label style="margin:0;">偏移量:</label>
                <input type="text" id="hexBatchOffset" value="0x33" style="width:80px;">
                <button onclick="hexBatchAddSub()">计算</button>
                <button onclick="clearHexBatch()" type="button">清空</button>
            </div>
            <div class="input-group">
                <label>结果:</label>
                <div class="row">
                    <textarea id="hexBatchResult" rows="4" readonly style="background:#f8f9fa;"></textarea>
                    <button class="copy-btn" onclick="copyResult('hexBatchResult')">复制</button>
                </div>
            </div>
            <div id="hexBatchError" class="error"></div>
        </div>

        <!-- Hex字节序切换 Tab -->
        <div class="tab-content" id="hex-endian-switch">
            <div class="input-group">
                <label for="hexEndianInput">Hex输入（偶数位）:</label>
                <div class="row">
                    <input type="text" id="hexEndianInput" placeholder="如 0102030A0BFF">
                    <button onclick="switchHexEndian()">切换大小端</button>
                    <button class="copy-btn" onclick="copyResult('hexEndianInput')">复制</button>
                </div>
                <div class="error" id="hexEndianError"></div>
            </div>
            <div class="input-group">
                <label>切换后结果:</label>
                <div class="row">
                    <input type="text" id="hexEndianResult" readonly>
                    <button class="copy-btn" onclick="copyResult('hexEndianResult')">复制</button>
                </div>
            </div>
        </div>

        <!-- 数据曲线拟合 Tab -->
        <div class="tab-content" id="data-fit">
            <div class="input-group">
                <label for="dataFitInput">输入数据（逗号、空格或换行分隔）:</label>
                <textarea id="dataFitInput" rows="4" placeholder="如 1.2, 2.3, 3.5 或每行一个"></textarea>
            </div>
            <div class="input-group row" style="align-items:center;gap:12px;">
                <button onclick="drawDataFitCurve()">拟合并绘制曲线</button>
                <button onclick="clearDataFitCurve()" type="button">清空</button>
            </div>
            <div id="dataFitError" class="error"></div>
            <div style="margin-top:16px;">
                <canvas id="dataFitCanvas" width="640" height="320" style="border:1px solid #b2dfdb;border-radius:6px;background:#f8f9fa;"></canvas>
            </div>
            <div id="dataFitInfo" style="color:#888;font-size:13px;margin-top:8px;"></div>
        </div>
        <!-- 中文编码转换 Tab -->
        <div class="tab-content" id="cn-encode">
            <div class="input-group">
                <label for="cnInput">中文/字符串输入:</label>
                <div class="row">
                    <input type="text" id="cnInput" placeholder="如 你好，世界">
                    <button onclick="cnUtf8ToHex()">UTF-8转Hex</button>
                    <button class="copy-btn" onclick="copyResult('cnInput')">复制</button>
                </div>
            </div>
            <div class="input-group">
                <label>Hex输入:</label>
                <div class="row">
                    <input type="text" id="cnHexInput" placeholder="如 E4BDA0E5A5BD">
                    <button onclick="hexToUtf8()">Hex转UTF-8</button>
                    <button class="copy-btn" onclick="copyResult('cnHexInput')">复制</button>
                </div>
            </div>
            <div style="color:#888;font-size:13px;">UTF-8转Hex/Hex转UTF-8可直接互填输入框，兼容所有现代浏览器。</div>
        </div>
        <!-- 移除第二排标签栏，只保留一排，所有工具标签横向滚动，整齐美观 -->
    </div>
    <div class="footer">© 2025 Pandur‘s嵌入式小工具集 <span style="color:#bbb;font-size:0.92em;margin-left:8px;">v1.0.2</span></div>
    <script>
        // Tab切换
        function showTab(tabId, btn) {
            document.querySelectorAll('.tab-content').forEach(content => {
                content.classList.remove('active');
            });
            document.getElementById(tabId).classList.add('active');
            document.querySelectorAll('.tab-btn').forEach(b => {
                b.classList.remove('active');
            });
            if (btn) btn.classList.add('active');
            // 自动聚焦第一个输入框
            setTimeout(() => {
                const activeTab = document.getElementById(tabId);
                const firstInput = activeTab.querySelector('input,textarea');
                if (firstInput) firstInput.focus();
            }, 100);
            // 清空所有操作提示
            const opMsg = document.getElementById('jsonOpMsg');
            if(opMsg) opMsg.textContent = '';
        }

// 中文编码转换相关（仅UTF-8，输入框互转）
function cnUtf8ToHex() {
    const str = document.getElementById('cnInput').value;
    if (!str) return;
    const encoder = new TextEncoder();
    const bytes = encoder.encode(str);
    let hex = Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase();
    document.getElementById('cnHexInput').value = hex;
}
function hexToUtf8() {
    const hex = document.getElementById('cnHexInput').value.replace(/\s+/g, '');
    if (!/^([0-9a-fA-F]{2})+$/.test(hex)) {
        document.getElementById('cnInput').value = 'Hex格式错误';
        return;
    }
    let bytes = new Uint8Array(hex.match(/.{2}/g).map(b => parseInt(b, 16)));
    try {
        const decoder = new TextDecoder('utf-8');
        document.getElementById('cnInput').value = decoder.decode(bytes);
    } catch (e) {
        document.getElementById('cnInput').value = '解码失败';
    }
}

// Hex字节序切换
function switchHexEndian() {
    const input = document.getElementById('hexEndianInput').value.replace(/\s+/g, '');
    const errorBox = document.getElementById('hexEndianError');
    const resultBox = document.getElementById('hexEndianResult');
    errorBox.textContent = '';
    resultBox.value = '';
    if (!input) {
        errorBox.textContent = '请输入Hex字符串';
        return;
    }
    if (!/^([0-9a-fA-F]{2})+$/.test(input)) {
        errorBox.textContent = '格式错误，需为偶数位Hex字符串';
        return;
    }
    // 按字节逆序
    let bytes = input.match(/.{2}/g);
    if (!bytes) {
        errorBox.textContent = '无有效字节';
        return;
    }
    let reversed = bytes.reverse().join('').toUpperCase();
    resultBox.value = reversed;
}
        // Hex-Float转换
        const floatInput = document.getElementById('floatInput');
        const hexInput = document.getElementById('hexInput');
        const floatError = document.getElementById('floatError');
        const hexError = document.getElementById('hexError');
        const endianSwitch = document.getElementById('endianSwitch');
        let isBigEndian = true;
        endianSwitch.addEventListener('change', () => {
            isBigEndian = endianSwitch.checked;
        });
        function hexToFloat() {
            const hex = hexInput.value.replace(/\s+/g, '').toUpperCase();
            hexError.textContent = '';
            floatError.textContent = '';
            if (!/^[0-9A-F]{8}$/.test(hex)) {
                hexError.textContent = '请输入8位有效十六进制数';
                floatInput.value = '';
                return;
            }
            try {
                const buffer = new ArrayBuffer(4);
                const view = new DataView(buffer);
                for (let i = 0; i < 4; i++) {
                    view.setUint8(i, parseInt(hex.substr(i*2,2), 16));
                }
                // 只用littleEndian参数，不再手动调整字节顺序
                const floatValue = view.getFloat32(0, !isBigEndian);
                floatInput.value = floatValue;
            } catch (error) {
                hexError.textContent = '转换失败';
                floatInput.value = '';
            }
        }
        function floatToHex() {
            const val = floatInput.value.trim();
            floatError.textContent = '';
            hexError.textContent = '';
            if (!val) {
                hexInput.value = '';
                return;
            }
            let f = parseFloat(val);
            if (!isFinite(f)) {
                floatError.textContent = '请输入有效的浮点数';
                hexInput.value = '';
                return;
            }
            try {
                const buffer = new ArrayBuffer(4);
                const view = new DataView(buffer);
                view.setFloat32(0, f, !isBigEndian);
                let hex = '';
                for (let i = 0; i < 4; i++) {
                    hex += view.getUint8(i).toString(16).padStart(2, '0');
                }
                hex = hex.toUpperCase();
                hexInput.value = hex;
            } catch (error) {
                floatError.textContent = '转换失败';
                hexInput.value = '';
            }
        }
        // ASCII-Hex转换
        function asciiToHex() {
            const ascii = document.getElementById('asciiInput').value;
            let hex = '';
            for (let i = 0; i < ascii.length; i++) {
                const charCode = ascii.charCodeAt(i);
                hex += charCode.toString(16).padStart(2, '0') + ' ';
            }
            document.getElementById('hexAsciiInput').value = hex.trimEnd();
        }
        function hexToAscii() {
            const hex = document.getElementById('hexAsciiInput').value.replace(/\s+/g, '');
            let ascii = '';
            if (hex.length % 2 !== 0) {
                return;
            }
            for (let i = 0; i < hex.length; i += 2) {
                const code = parseInt(hex.substr(i, 2), 16);
                if (isNaN(code)) {
                    return;
                }
                ascii += String.fromCharCode(code);
            }
            document.getElementById('asciiInput').value = ascii;
        }
        // 更新日期格式标签
        function updateDateFormatLabel() {
            const mode = document.querySelector('input[name="timestampMode"]:checked').value;
            const label = document.getElementById('dateInputLabel');
            if (mode === 'milliseconds') {
                label.textContent = '日期输入（YYYY-MM-DD HH:mm:ss.SSS）:';
            } else {
                label.textContent = '日期输入（YYYY-MM-DD HH:mm:ss）:';
            }
        }
        
        // 时间戳-日期转换
        function timestampToDate() {
            const timestamp = document.getElementById('timestampInput').value;
            if (!timestamp || isNaN(Number(timestamp))) {
                return;
            }
            const ts = Number(timestamp);
            // 获取用户选择的模式
            const mode = document.querySelector('input[name="timestampMode"]:checked').value;
            const isMs = mode === 'milliseconds';
            const date = new Date(isMs ? ts : ts * 1000);
            let formatted;
            if (mode === 'milliseconds') {
                formatted = `${date.getFullYear()}-${String(date.getMonth()+1).padStart(2,'0')}-${String(date.getDate()).padStart(2,'0')} ${String(date.getHours()).padStart(2,'0')}:${String(date.getMinutes()).padStart(2,'0')}:${String(date.getSeconds()).padStart(2,'0')}.${String(date.getMilliseconds()).padStart(3,'0')}`;
            } else {
                formatted = `${date.getFullYear()}-${String(date.getMonth()+1).padStart(2,'0')}-${String(date.getDate()).padStart(2,'0')} ${String(date.getHours()).padStart(2,'0')}:${String(date.getMinutes()).padStart(2,'0')}:${String(date.getSeconds()).padStart(2,'0')}`;
            }
            document.getElementById('dateInput').value = formatted;
        }
        function dateToTimestamp() {
            const dateStr = document.getElementById('dateInput').value;
            if (!dateStr) {
                return;
            }
            const timestamp = new Date(dateStr).getTime();
            if (isNaN(timestamp)) {
                return;
            }
            // 获取用户选择的模式
            const mode = document.querySelector('input[name="timestampMode"]:checked').value;
            const timestampSec = Math.floor(timestamp / 1000);
            const timestampMs = timestamp;
            
            // 根据选择的模式输出对应的时间戳
            if (mode === 'milliseconds') {
                document.getElementById('timestampInput').value = timestampMs;
            } else {
                document.getElementById('timestampInput').value = timestampSec;
            }
        }
        // Hex-C数组互转
        function hexToCArray() {
            const input = document.getElementById('hexCArrayInput').value.replace(/\s+/g, '');
            const errorBox = document.getElementById('hexCArrayError');
            errorBox.textContent = '';
            if (!/^([0-9a-fA-F]{2})+$/.test(input)) {
                errorBox.textContent = '请输入有效的Hex字符串（偶数位）';
                document.getElementById('hexCArrayResult').value = '';
                return;
            }
            let arr = [];
            for (let i = 0; i < input.length; i += 2) {
                arr.push('0x' + input.substr(i, 2).toUpperCase());
            }
            document.getElementById('cArrayHexInput').value = arr.join(',');
            document.getElementById('hexCArrayResult').value = arr.join(',');
        }
        function cArrayToHex() {
            const input = document.getElementById('cArrayHexInput').value.trim();
            const errorBox = document.getElementById('cArrayHexError');
            errorBox.textContent = '';
            if (!input) {
                errorBox.textContent = '请输入C数组格式内容（如 0x01,0x02,0x0A,0xFF）';
                document.getElementById('cArrayHexResult').value = '';
                return;
            }
            let arr = input.split(',').map(s => s.trim()).filter(Boolean);
            let hexStr = '';
            for (let s of arr) {
                let m = s.match(/^0x([0-9a-fA-F]{1,2})$/);
                if (!m) {
                    errorBox.textContent = '格式错误，需如 0x01,0x0A,0xFF';
                    document.getElementById('cArrayHexResult').value = '';
                    return;
                }
                hexStr += m[1].toUpperCase().padStart(2, '0');
            }
            document.getElementById('hexCArrayInput').value = hexStr;
            document.getElementById('cArrayHexResult').value = hexStr;
        }
        // 字节数计算
        function countHexBytes() {
            const inputBox = document.getElementById('hexByteCountInput');
            const errorBox = document.getElementById('hexByteCountError');
            const resultBox = document.getElementById('hexByteCountResult');
            errorBox.textContent = '';
            resultBox.value = '';
            let input = inputBox.value.replace(/\s+/g, '');
            if (!input) {
                errorBox.textContent = '请输入Hex字符串';
                return;
            }
            if (!/^([0-9a-fA-F]{2})+$/.test(input)) {
                errorBox.textContent = '格式错误，需为偶数位Hex字符串';
                return;
            }
            resultBox.value = input.length / 2;
            // 自动格式化为带空格的Hex
            inputBox.value = input.match(/.{1,2}/g).join(' ');
        }
        // C语言编码转换示例相关JS
        const cEncodeCodeMap = {
            hex2bcd: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief 将十六进制数转换为BCD码
 * @param hex 输入的十六进制数
 * @param bcd 输出的BCD数组，需至少1字节
 */
void hex2bcd(uint8_t hex, uint8_t *bcd) {
    bcd[0] = ((hex / 10) << 4) | (hex % 10);
}`
            },
            bcd2hex: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief 将BCD码转换为十六进制数
 * @param bcd 输入的BCD码
 * @return 转换后的十六进制数
 */
uint8_t bcd2hex(uint8_t bcd) {
    return ((bcd >> 4) * 10) + (bcd & 0x0F);
}`
            },
            hex2str: {
                headers: ['#include <stdint.h>', '#include <stdio.h>'],
                code: `/**
 * @brief 将Hex数组转换为字符串（如0x12,0x34 -> "1234"）
 * @param hex 输入的Hex数组
 * @param len 数组长度
 * @param str 输出字符串，需至少2*len+1字节
 */
void hex2str(const uint8_t *hex, int len, char *str) {
    int i;
    for(i = 0; i < len; i++) {
        sprintf(str + 2 * i, "%02X", hex[i]);
    }
    str[2 * len] = '\0';
}`
            },
            str2hex: {
                headers: ['#include <stdint.h>', '#include <stdio.h>', '#include <string.h>'],
                code: `/**
 * @brief 将字符串转换为Hex数组（如"1234"->0x12,0x34）
 * @param str 输入字符串（偶数字符）
 * @param hex 输出Hex数组
 * @return 转换后的字节数
 */
int str2hex(const char *str, uint8_t *hex) {
    int i, len;
    len = strlen(str) / 2;
    for(i = 0; i < len; i++) {
        unsigned int val;
        sscanf(str + 2 * i, "%2x", &val);
        hex[i] = (uint8_t)val;
    }
    return len;
}`
            },
            bcd2str: {
                headers: ['#include <stdint.h>', '#include <stdio.h>'],
                code: `/**
 * @brief 将BCD码转换为字符串（如0x12->"12"）
 * @param bcd 输入的BCD码
 * @param str 输出字符串，需至少3字节
 */
void bcd2str(uint8_t bcd, char *str) {
    sprintf(str, "%d%d", (bcd >> 4) & 0x0F, bcd & 0x0F);
}`
            },
            str2bcd: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief 将字符串转换为BCD码（如"12"->0x12）
 * @param str 输入字符串（2位数字）
 * @return 转换后的BCD码
 */
uint8_t str2bcd(const char *str) {
    return ((str[0] - '0') << 4) | (str[1] - '0');
}`
            },
            itoa: {
                headers: ['#include <stdio.h>'],
                code: `/**
 * @brief int转字符串（标准C实现）
 * @param num 输入整数
 * @param str 输出字符串，需足够空间
 */
void my_itoa(int num, char *str) {
    sprintf(str, "%d", num);
}`
            },
            atoi: {
                headers: ['#include <stdio.h>'],
                code: `/**
 * @brief 字符串转int
 * @param str 输入字符串
 * @return 转换后的整数
 */
int my_atoi(const char *str) {
    int val = 0;
    sscanf(str, "%d", &val);
    return val;
}`
            },
            ftoa: {
                headers: ['#include <stdio.h>'],
                code: `/**
 * @brief float转字符串
 * @param f 输入浮点数
 * @param str 输出字符串，需足够空间
 */
void my_ftoa(float f, char *str) {
    sprintf(str, "%f", f);
}`
            },
            atof: {
                headers: ['#include <stdio.h>'],
                code: `/**
 * @brief 字符串转float
 * @param str 输入字符串
 * @return 转换后的浮点数
 */
float my_atof(const char *str) {
    float val = 0.0f;
    sscanf(str, "%f", &val);
    return val;
}`
            },
            hexstr2int: {
                headers: ['#include <stdio.h>'],
                code: `/**
 * @brief 16进制字符串转int（如"1A3F"->6719）
 * @param str 输入16进制字符串
 * @return 转换后的整数
 */
int hexstr2int(const char *str) {
    int val = 0;
    sscanf(str, "%x", &val);
    return val;
}`
            },
            int2hexstr: {
                headers: ['#include <stdio.h>'],
                code: `/**
 * @brief int转16进制字符串
 * @param val 输入整数
 * @param str 输出字符串，需足够空间
 */
void int2hexstr(int val, char *str) {
    sprintf(str, "%X", val);
}`
            },
            swap16: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief 16位大小端转换
 * @param v 输入16位整数
 * @return 大小端交换后的值
 */
uint16_t swap16(uint16_t v) {
    return (uint16_t)(((v >> 8) & 0xFF) | ((v & 0xFF) << 8));
}`
            },
            swap32: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief 32位大小端转换
 * @param v 输入32位整数
 * @return 大小端交换后的值
 */
uint32_t swap32(uint32_t v) {
    return ((v >> 24) & 0xFF) |
           ((v >> 8) & 0xFF00) |
           ((v & 0xFF00) << 8) |
           ((v & 0xFF) << 24);
}`
            },
            bytes2u16: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief 字节数组转uint16（大端）
 * @param b 输入字节数组，长度>=2
 * @return 转换后的uint16值
 */
uint16_t bytes2u16(const uint8_t *b) {
    return ((uint16_t)b[0] << 8) | b[1];
}`
            },
            bytes2u32: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief 字节数组转uint32（大端）
 * @param b 输入字节数组，长度>=4
 * @return 转换后的uint32值
 */
uint32_t bytes2u32(const uint8_t *b) {
    return ((uint32_t)b[0] << 24) |
           ((uint32_t)b[1] << 16) |
           ((uint32_t)b[2] << 8) |
           ((uint32_t)b[3]);
}`
            },
            u16tobytes: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief uint16转字节数组（大端）
 * @param v 输入uint16值
 * @param b 输出字节数组，长度>=2
 */
void u16tobytes(uint16_t v, uint8_t *b) {
    b[0] = (uint8_t)((v >> 8) & 0xFF);
    b[1] = (uint8_t)(v & 0xFF);
}`
            },
            u32tobytes: {
                headers: ['#include <stdint.h>'],
                code: `/**
 * @brief uint32转字节数组（大端）
 * @param v 输入uint32值
 * @param b 输出字节数组，长度>=4
 */
void u32tobytes(uint32_t v, uint8_t *b) {
    b[0] = (uint8_t)((v >> 24) & 0xFF);
    b[1] = (uint8_t)((v >> 16) & 0xFF);
    b[2] = (uint8_t)((v >> 8) & 0xFF);
    b[3] = (uint8_t)(v & 0xFF);
}`
            }
        };
        function showCEncodeCode() {
            const sel = document.getElementById('cEncodeSelect').value;
            const item = cEncodeCodeMap[sel];
            if (!item) {
                document.getElementById('cEncodeCodeBox').value = '';
                return;
            }
            // 合并头文件和代码
            let headers = Array.isArray(item.headers) ? item.headers : [];
            let code = item.code || '';
            let out = '';
            if (headers.length > 0) {
                out += headers.join('\n') + '\n';
            }
            out += code;
            document.getElementById('cEncodeCodeBox').value = out;
        }
        // 页面加载时默认显示第一个代码
        window.addEventListener('DOMContentLoaded', showCEncodeCode);
        // 扩展copyResult支持所有Tab
        // 复制按钮逻辑调整，直接复制目标输入框/textarea内容
        function copyResult(id) {
            const el = document.getElementById(id);
            if (!el) return;
            let val = '';
            // 针对input/textarea优先取value，其他元素取textContent
            if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
                val = el.value;
            } else {
                val = el.textContent;
            }
            if (!val) return;
            let copyPromise;
            if (navigator.clipboard && window.isSecureContext) {
                copyPromise = navigator.clipboard.writeText(val);
            } else {
                const temp = document.createElement('textarea');
                temp.value = val;
                document.body.appendChild(temp);
                temp.select();
                try {
                    document.execCommand('copy');
                    copyPromise = Promise.resolve();
                } catch (e) {
                    copyPromise = Promise.reject(e);
                }
                document.body.removeChild(temp);
            }
            // 复制结果提示（仅在hash-tool Tab显示，其他Tab可扩展）
            const hashCopyMsg = document.getElementById('hashCopyMsg');
            if (hashCopyMsg) {
                Promise.resolve(copyPromise).then(() => {
                    hashCopyMsg.textContent = '已复制';
                    hashCopyMsg.style.color = '#1a7f37';
                    setTimeout(() => { hashCopyMsg.textContent = ''; }, 1200);
                }).catch(() => {
                    hashCopyMsg.textContent = '复制失败';
                    hashCopyMsg.style.color = '#e53935';
                    setTimeout(() => { hashCopyMsg.textContent = ''; }, 1200);
                });
            }
        }
        // 文件读取为ArrayBuffer（用于哈希/校验文件输入）
        function readFileAsArrayBuffer(file) {
            return new Promise((resolve, reject) => {
                const reader = new FileReader();
                reader.onload = function(e) {
                    resolve(e.target.result);
                };
                reader.onerror = function(e) {
                    reject(e);
                };
                reader.readAsArrayBuffer(file);
            });
        }
        
        // 大文件分块读取和哈希计算
        async function calculateLargeFileHash(file) {
            const CHUNK_SIZE = 2 * 1024 * 1024; // 2MB chunks
            const fileSize = file.size;
            let offset = 0;
            
            // 初始化哈希计算器
            let md5Hasher = null;
            let sha1Hasher = null;
            let sha256Hasher = null;
            let crc16Value = 0x0000;
            let crc32Value = 0xFFFFFFFF;
            let sumValue = 0;
            
            if (window.CryptoJS && CryptoJS.MD5) {
                md5Hasher = CryptoJS.algo.MD5.create();
                sha1Hasher = CryptoJS.algo.SHA1.create();
                sha256Hasher = CryptoJS.algo.SHA256.create();
            }
            
            // 显示计算进度
            const progressElement = document.getElementById('hashFileName');
            const originalText = progressElement.textContent;
            
            while (offset < fileSize) {
                const chunk = file.slice(offset, Math.min(offset + CHUNK_SIZE, fileSize));
                const arrayBuffer = await readFileAsArrayBuffer(chunk);
                const chunkData = new Uint8Array(arrayBuffer);
                
                // 更新进度
                const progress = Math.round((offset / fileSize) * 100);
                progressElement.textContent = `文件名：${file.name} (计算中: ${progress}%)`;
                
                // 更新各种哈希值
                if (md5Hasher) {
                    const wordArray = CryptoJS.lib.WordArray.create(chunkData);
                    md5Hasher.update(wordArray);
                    sha1Hasher.update(wordArray);
                    sha256Hasher.update(wordArray);
                }
                
                // 更新CRC16
                for (let i = 0; i < chunkData.length; i++) {
                    crc16Value ^= (chunkData[i] << 8);
                    for (let j = 0; j < 8; j++) {
                        if (crc16Value & 0x8000) {
                            crc16Value = (crc16Value << 1) ^ 0x1021;
                        } else {
                            crc16Value = crc16Value << 1;
                        }
                        crc16Value &= 0xFFFF;
                    }
                }
                
                // 更新CRC32
                for (let i = 0; i < chunkData.length; i++) {
                    crc32Value ^= chunkData[i];
                    for (let j = 0; j < 8; j++) {
                        if (crc32Value & 1) {
                            crc32Value = (crc32Value >>> 1) ^ 0xEDB88320;
                        } else {
                            crc32Value = crc32Value >>> 1;
                        }
                    }
                }
                
                // 更新和校验
                for (let i = 0; i < chunkData.length; i++) {
                    sumValue = (sumValue + chunkData[i]) & 0xFF;
                }
                
                offset += CHUNK_SIZE;
                
                // 让UI有机会更新
                await new Promise(resolve => setTimeout(resolve, 0));
            }
            
            // 恢复原始文件名显示
            progressElement.textContent = originalText;
            
            // 返回计算结果
            return {
                md5: md5Hasher ? md5Hasher.finalize().toString() : 'CryptoJS库未加载',
                sha1: sha1Hasher ? sha1Hasher.finalize().toString() : 'CryptoJS库未加载',
                sha256: sha256Hasher ? sha256Hasher.finalize().toString() : 'CryptoJS库未加载',
                crc16: crc16Value.toString(16).toUpperCase().padStart(4, '0'),
                crc32: ((crc32Value ^ 0xFFFFFFFF) >>> 0).toString(16).toUpperCase().padStart(8, '0'),
                sum: sumValue.toString(16).toUpperCase().padStart(2, '0')
            };
        }
        // ===== 检查CryptoJS库，如未引入则自动加载 =====
        // 优先本地加载 CryptoJS，离线优先
        if (!window.CryptoJS || !CryptoJS.MD5) {
            var script = document.createElement('script');
            script.src = './crypto-js.min.js'; // 本地优先
            script.onload = function() {
                // 本地加载成功，无需提示
            };
            script.onerror = function() {
                // 本地加载失败再尝试CDN
                var cdnScript = document.createElement('script');
                cdnScript.src = 'https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/crypto-js.min.js';
                cdnScript.onload = function(){};
                cdnScript.onerror = function(){
                    alert('CryptoJS库加载失败，MD5功能不可用');
                };
                document.head.appendChild(cdnScript);
            };
            document.head.appendChild(script);
        }
        // 保留的通用函数
        function arrayBufferToWordArray(ab) {
            const i8a = new Uint8Array(ab);
            const a = [];
            for (let i = 0; i < i8a.length; i += 4) {
                a.push(
                    ((i8a[i] << 24) | (i8a[i + 1] << 16) | (i8a[i + 2] << 8) | (i8a[i + 3] || 0)) >>> 0
                );
            }
            return CryptoJS.lib.WordArray.create(a, i8a.length);
        }
        // 动态分配标签到多排，保证每排最多3个，超出自动放到下一排
        function arrangeTabs() {
            const allTabs = Array.from(document.querySelectorAll('.tab-btn'));
            const row1 = document.getElementById('tabs-row-1');
            const row2 = document.getElementById('tabs-row-2');
            if (!row1 || !row2) return;
            // 清空第二排
            row2.innerHTML = '';
            let count = 0;
            allTabs.forEach(tab => {
                if (count < 3) {
                    row1.appendChild(tab);
                } else {
                    row2.appendChild(tab);
                }
                count++;
            });
            row2.style.display = row2.children.length > 0 ? 'flex' : 'none';
        }
        // 页面加载后自动整理标签
        window.addEventListener('DOMContentLoaded', arrangeTabs);
        // 页面加载后自动输出一次JSON字符串数组（防止刷新后为空）
        window.addEventListener('DOMContentLoaded', updateJsonStringArray);
        // 监听哈希输入框和文件输入变化，自动计算哈希
        document.getElementById('hashInput').addEventListener('input', calcUnifiedHash);
        document.getElementById('hashFileInput').addEventListener('change', calcUnifiedHash);
        // 兼容页面初次加载时自动计算一次
        window.addEventListener('DOMContentLoaded', calcUnifiedHash);
        // 统一哈希/校验计算函数
        async function calcUnifiedHash() {
            const type = document.querySelector('input[name="hashInputType"]:checked').value;
            let data = null;
            let buf = null;
            if (type === 'text') {
                data = document.getElementById('hashInput').value;
                document.getElementById('hashFileName').textContent = '';
                // 新增：解析方式
                const parseMode = document.querySelector('input[name="hashParseMode"]:checked').value;
                if (parseMode === 'hex') {
                    let hexStr = data.replace(/\s+/g, '');
                    if (!/^([0-9a-fA-F]{2})+$/.test(hexStr) || hexStr.length < 2) {
                        clearUnifiedHashResult();
                        return;
                    }
                    let arr = [];
                    for (let i = 0; i < hexStr.length; i += 2) {
                        arr.push(parseInt(hexStr.substr(i, 2), 16));
                    }
                    buf = new Uint8Array(arr);
                } else {
                    // 按字符串直接转为UTF-8字节流
                    buf = new TextEncoder().encode(data);
                    if (!buf || buf.length === 0) {
                        clearUnifiedHashResult();
                        return;
                    }
                }
            } else {
                const fileInput = document.getElementById('hashFileInput');
                const file = fileInput.files[0];
                if (!file) {
                    document.getElementById('hashFileName').textContent = '请先选择一个文件';
                    clearUnifiedHashResult();
                    return;
                }
                document.getElementById('hashFileName').textContent = `文件名：${file.name}`;
                
                // 显示文件大小信息
                const fileSizeInMB = (file.size / (1024 * 1024)).toFixed(2);
                const fileInfoElement = document.getElementById('hashFileInfo');
                
                // 根据文件大小选择处理方式
                const LARGE_FILE_THRESHOLD = 50 * 1024 * 1024; // 50MB
                
                if (file.size > LARGE_FILE_THRESHOLD) {
                    // 大文件使用分块处理
                    fileInfoElement.textContent = `文件大小：${fileSizeInMB} MB (大文件分块处理中...)`;
                    fileInfoElement.style.color = '#ff6b35';
                    
                    const results = await calculateLargeFileHash(file);
                    document.getElementById('md5UnifiedOutput').value = results.md5;
                    document.getElementById('sha1UnifiedOutput').value = results.sha1;
                    document.getElementById('sha256UnifiedOutput').value = results.sha256;
                    document.getElementById('crc16UnifiedOutput').value = results.crc16;
                    document.getElementById('crc32UnifiedOutput').value = results.crc32;
                    document.getElementById('sumUnifiedOutput').value = results.sum;
                    
                    // 处理完成后更新信息
                    fileInfoElement.textContent = `文件大小：${fileSizeInMB} MB (大文件分块处理完成)`;
                    fileInfoElement.style.color = '#1a7f37';
                    return; // 直接返回，不再执行后续的统一计算逻辑
                } else {
                    // 小文件使用原来的处理方式
                    fileInfoElement.textContent = `文件大小：${fileSizeInMB} MB (小文件快速处理)`;
                    fileInfoElement.style.color = '#666';
                    
                    data = await readFileAsArrayBuffer(file);
                    buf = new Uint8Array(data);
                    if (buf.length === 0) {
                        clearUnifiedHashResult();
                        return;
                    }
                }
            }
            if (!buf || buf.length === 0) {
                clearUnifiedHashResult();
                return;
            }
            // 计算三种哈希
            // MD5
            if (window.CryptoJS && CryptoJS.MD5) {
                let wordArray = CryptoJS.lib.WordArray.create(buf);
                // 修正：CryptoJS默认toString()为小写，和主流工具一致，若需大写可加.toUpperCase()
                document.getElementById('md5UnifiedOutput').value = CryptoJS.MD5(wordArray).toString();
                document.getElementById('sha1UnifiedOutput').value = CryptoJS.SHA1(wordArray).toString();
                document.getElementById('sha256UnifiedOutput').value = CryptoJS.SHA256(wordArray).toString();
            } else {
                document.getElementById('md5UnifiedOutput').value = 'CryptoJS库未加载';
                document.getElementById('sha1UnifiedOutput').value = 'CryptoJS库未加载';
                document.getElementById('sha256UnifiedOutput').value = 'CryptoJS库未加载';
            }
            // CRC16-CCITT (XModem)
            document.getElementById('crc16UnifiedOutput').value = crc16ccitt(buf);
            // CRC32
            document.getElementById('crc32UnifiedOutput').value = crc32(buf);
            // 和校验
            document.getElementById('sumUnifiedOutput').value = sumCheck(buf);
        }
        // 清空哈希结果
        function clearUnifiedHashResult() {
            document.getElementById('md5UnifiedOutput').value = '';
            document.getElementById('sha1UnifiedOutput').value = '';
            document.getElementById('sha256UnifiedOutput').value = '';
            document.getElementById('crc16UnifiedOutput').value = '';
            document.getElementById('crc32UnifiedOutput').value = '';
            document.getElementById('sumUnifiedOutput').value = '';
        }
        // CRC16-CCITT (XModem) 实现
        function crc16ccitt(buf) {
            let crc = 0x0000;
            for (let i = 0; i < buf.length; i++) {
                crc ^= (buf[i] << 8);
                for (let j = 0; j < 8; j++) {
                    if (crc & 0x8000) {
                        crc = (crc << 1) ^ 0x1021;
                    } else {
                        crc = crc << 1;
                    }
                    crc &= 0xFFFF;
                }
            }
            return crc.toString(16).toUpperCase().padStart(4, '0');
        }
        // CRC32实现
        function crc32(buf) {
            let crc = 0xFFFFFFFF;
            for (let i = 0; i < buf.length; i++) {
                crc ^= buf[i];
                for (let j = 0; j < 8; j++) {
                    if (crc & 1) {
                        crc = (crc >>> 1) ^ 0xEDB88320;
                    } else {
                        crc = crc >>> 1;
                    }
                }
            }
            // 修正：确保输出为8位大写正数Hex
            return ((crc ^ 0xFFFFFFFF) >>> 0).toString(16).toUpperCase().padStart(8, '0');
        }
        // 和校验
        function sumCheck(buf) {
            let sum = 0;
            for (let i = 0; i < buf.length; i++) {
                sum = (sum + buf[i]) & 0xFF;
            }
            return sum.toString(16).toUpperCase().padStart(2, '0');
        }
        // JSON组装相关变量和函数
        let jsonHistory = [];
        function addJsonPair() {
            let key = document.getElementById('jsonKey').value.trim();
            let path = document.getElementById('jsonPath').value.trim();
            let valueInput = document.getElementById('jsonValue').value.trim();
            let valueType = document.getElementById('jsonValueType').value;
            const opMsg = document.getElementById('jsonOpMsg');
            if (!key) {
                opMsg.textContent = '键名不能为空！';
                opMsg.style.color = '#e53935';
                return;
            }
            // 路径为空时直接在根对象添加
            let pathSegments = path ? path.split(/\.|\[|\]/).filter(seg => seg !== '') : [];
            let value;
            try {
                switch (valueType) {
                    case 'number':
                        value = Number(valueInput);
                        break;
                    case 'boolean':
                        value = valueInput.toLowerCase() === 'true';
                        break;
                    case 'null':
                        value = null;
                        break;
                    case 'array':
                        value = JSON.parse(`[${valueInput}]`);
                        break;
                    case 'object':
                        value = JSON.parse(`{${valueInput}}`);
                        break;
                    default:
                        value = valueInput;
                }
            } catch (e) {
                opMsg.textContent = '值格式无效，请检查输入！';
                opMsg.style.color = '#e53935';
                return;
            }
            let currentObj = {};
            try {
                currentObj = JSON.parse(document.getElementById('jsonResult').value || '{}');
            } catch (e) {}
            jsonHistory.push(JSON.stringify(currentObj));
            let targetObj = currentObj;
            if (pathSegments.length === 0) {
                // 直接在根对象添加
                targetObj[key] = value;
            } else {
                for (let i = 0; i < pathSegments.length - 1; i++) {
                    const seg = pathSegments[i];
                    const nextSeg = pathSegments[i + 1];
                    // 判断下一个是否为数字（数组下标）
                    const isNextArrayIndex = nextSeg && /^\d+$/.test(nextSeg);
                    if (/^\d+$/.test(seg)) {
                        // 当前为数组下标
                        const idx = parseInt(seg);
                        if (!Array.isArray(targetObj)) {
                            // 如果不是数组，转为数组
                            targetObj = [];
                        }
                        if (!targetObj[idx]) targetObj[idx] = isNextArrayIndex ? [] : {};
                        targetObj = targetObj[idx];
                    } else {
                        // 当前为对象key
                        if (!targetObj[seg]) {
                            targetObj[seg] = isNextArrayIndex ? [] : {};
                        }
                        targetObj = targetObj[seg];
                    }
                }
                const lastSeg = pathSegments[pathSegments.length - 1];
                if (/^\d+$/.test(lastSeg)) {
                    // 最后一级为数组下标
                    const idx = parseInt(lastSeg);
                    if (!Array.isArray(targetObj)) targetObj = [];
                    if (!targetObj[idx]) targetObj[idx] = {};
                    targetObj[idx][key] = value;
                } else {
                    targetObj[lastSeg] = {
                        ...targetObj[lastSeg],
                        [key]: value
                    };
                }
            }
            document.getElementById('jsonResult').value = JSON.stringify(currentObj, null, 2);
            document.getElementById('jsonKey').value = '';
            document.getElementById('jsonValue').value = '';
            document.getElementById('jsonPath').value = '';
            opMsg.textContent = '添加成功！';
            opMsg.style.color = '#26a69a';
            setTimeout(()=>{opMsg.textContent='';}, 1500);
            setTimeout(()=>{
                document.getElementById('jsonResult').scrollIntoView({behavior:'smooth',block:'center'});
            }, 200);
            // 新增：自动输出JSON字符串数组
            updateJsonStringArray();
        }
        function undoJson() {
            const opMsg = document.getElementById('jsonOpMsg');
            if (jsonHistory.length === 0) {
                opMsg.textContent = '没有可撤销的操作';
                opMsg.style.color = '#e53935';
                return;
            }
            document.getElementById('jsonResult').value = jsonHistory.pop();
            opMsg.textContent = '已撤销上一步';
            opMsg.style.color = '#26a69a';
            setTimeout(()=>{opMsg.textContent='';}, 1200);
            updateJsonStringArray();
        }
        function previewJson() {
            const opMsg = document.getElementById('jsonOpMsg');
            let val = document.getElementById('jsonResult').value;
            try {
                const obj = JSON.parse(val);
                document.getElementById('jsonResult').value = JSON.stringify(obj, null, 2);
                opMsg.textContent = 'JSON格式正确';
                opMsg.style.color = '#26a69a';
            } catch (e) {
                opMsg.textContent = 'JSON格式有误：' + e.message;
                opMsg.style.color = '#e53935';
            }
            setTimeout(()=>{opMsg.textContent='';}, 2000);
            updateJsonStringArray();
        }
        function updateJsonStringArray() {
            let val = document.getElementById('jsonResult').value;
            let str = '';
            try {
                const obj = JSON.parse(val);
                // 整个对象的JSON字符串作为数组元素
                str = '[' + JSON.stringify(JSON.stringify(obj)) + ']';
            } catch (e) {
                str = '';
            }
            document.getElementById('jsonStringArrayResult').value = str;
        }
        function clearJson() {
            document.getElementById('jsonResult').value = '';
            document.getElementById('jsonStringArrayResult').value = '';
            jsonHistory = [];
            document.getElementById('jsonOpMsg').textContent = '';
        }
        // 资源占用分析Tab
        function analyzeMapFile() {
            const fileInput = document.getElementById('mapFileInput');
            const file = fileInput.files[0];
            const fileNameBox = document.getElementById('mapFileName');
            const resultBox = document.getElementById('resourceAnalyzeResult');
            const tableWrap = document.getElementById('resourceAnalyzeTableWrap');
            const msgBox = document.getElementById('resourceAnalyzeMsg');
            fileNameBox.textContent = '';
            resultBox.value = '';
            tableWrap.innerHTML = '';
            msgBox.textContent = '';
            if (!file) {
                msgBox.textContent = '请上传map文件（无需.a文件）';
                return;
            }
            fileNameBox.textContent = `文件名：${file.name}`;
            const reader = new FileReader();
            reader.onload = function(e) {
                const text = e.target.result;
                const analyze = parseMapFile(text);
                if (!analyze || !analyze.rows) {
                    msgBox.textContent = '未能识别map文件内容或无有效数据';
                    resultBox.value = '';
                    tableWrap.innerHTML = '';
                    return;
                }
                if (analyze.rows.length === 0) {
                    resultBox.value = '未能识别map文件中的对象/段信息，或文件格式不兼容。\n请确认map文件为标准GCC/ARMCC/IAR等链接器输出。';
                    tableWrap.innerHTML = '';
                    msgBox.textContent = '无有效对象/段信息';
                    return;
                }
                // 输出表格（优化长路径省略和防止乱码）
                let html = '<table class="hash-table" style="margin-top:8px;table-layout:fixed;width:100%;"><tr><th style="max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">对象/函数</th><th>Section</th><th>大小(字节)</th></tr>';
                let sectionStat = {};
                for (const row of analyze.rows) {
                    let displayName = row.name;
                    let maxLen = 36;
                    if (displayName.length > maxLen) {
                        displayName = displayName.slice(0, 15) + '…' + displayName.slice(-15);
                    }
                    html += `<tr><td title="${row.name}" style="max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${displayName}</td><td>${row.section}</td><td>${row.size}</td></tr>`;
                    if (row.section) {
                        if (!sectionStat[row.section]) sectionStat[row.section] = 0;
                        sectionStat[row.section] += row.size;
                    }
                }
                html += '</table>';
                tableWrap.innerHTML = html;
                let total = 0;
                let objSet = new Set();
                for (const row of analyze.rows) {
                    total += row.size;
                    let m = row.name.match(/([\w\-\.]+\.o)/);
                    if (m) objSet.add(m[1]);
                }
                let objCount = objSet.size;
                let objList = Array.from(objSet).join(', ');
                // 只归纳展示常用Section，去除.init和.fini
                const commonSections = ['.text', '.data', '.bss', '.rodata', '.constdata', '.const', '.code', 'COMMON'];
                let sectionLines = ['常用Section资源占用:'];
                let uncommonLines = [];
                Object.keys(sectionStat).sort().forEach(sec => {
                    if (commonSections.includes(sec.toLowerCase())) {
                        sectionLines.push(`${sec}: ${sectionStat[sec]} 字节`);
                    } else {
                        uncommonLines.push(`${sec}: ${sectionStat[sec]} 字节`);
                    }
                });
                let summaryText = sectionLines.join('\n');
                summaryText += `\n总占用: ${total} 字节\n对象文件数: ${objCount}`;
                if (objCount > 0) summaryText += `\n对象文件示例: ${objList.slice(0, 100)}${objList.length>100?'...':''}`;
                summaryText += '\n\n对象文件即.o文件，通常对应每个C源文件编译后的产物，如main.o、utils.o等。';
                if (analyze.summary) summaryText += `\n${analyze.summary}`;
                // 如有不常用Section，提示可在表格中查看
                if (uncommonLines.length > 0) {
                    summaryText += '\n(部分不常用Section未在此归纳展示，详见下方表格)';
                }
                resultBox.value = summaryText;
                msgBox.textContent = '分析完成';
                tableWrap.innerHTML += '<div style="color:#888;font-size:13px;margin-top:4px;">对象文件即.o文件，通常对应每个C源文件编译后的产物。若需详细含义请参考工程源码结构。</div>';
            };
            reader.onerror = function() {
                msgBox.textContent = '读取文件失败';
            };
            reader.readAsText(file);
        }
        // 新增：map文件分析支持删除/清空
        function clearMapFile() {
            document.getElementById('mapFileInput').value = '';
            document.getElementById('mapFileName').textContent = '';
            document.getElementById('resourceAnalyzeResult').value = '';
            document.getElementById('resourceAnalyzeTableWrap').innerHTML = '';
            document.getElementById('resourceAnalyzeMsg').textContent = '';
        }
        // map文件多格式兼容解析
        function parseMapFile(text) {
            const lines = text.split(/\r?\n/);
            const rows = [];
            let summary = '';
            // 兼容GCC/ARMCC/IAR等常见格式
            // 1. GCC:  .text          0x08000100     0x1c main.o
            // 2. ARMCC: .text  0x00008000   0x00000020   main.o(.text)
            // 3. IAR:   .text  0x08000100   0x0000001c   main.o [1]
            // 4. 兼容部分函数级别:  .text.func  0x08000100 0x20 main.o(func)
            // 5. 兜底：Section Name  Address  Size  Object
            const sectionRegexs = [
                // GCC/ARMCC/IAR常见格式
                /^(\.[\w\.]+)\s+0x[0-9a-fA-F]+\s+0x([0-9a-fA-F]+)\s+([\w\-\.]+\.o)(?:[\(\[]([^\)\]]+)[\)\]])?/,
                // IAR: .text  0x08000100   0x0000001c   main.o [1]
                /^(\.[\w\.]+)\s+0x[0-9a-fA-F]+\s+0x([0-9a-fA-F]+)\s+([\w\-\.]+\.o)(?:\s+\[\d+\])?$/,
                // ARMCC: .text  0x00008000   0x00000020   main.o(.text)
                /^(\.[\w\.]+)\s+0x[0-9a-fA-F]+\s+0x([0-9a-fA-F]+)\s+([\w\-\.]+\.o)\(([^\)]+)\)/,
                // 兜底：Section Name  Address  Size  Object
                /^(\.[\w\.]+)\s+0x[0-9a-fA-F]+\s+0x([0-9a-fA-F]+)\s+([\w\-\.]+\.o)$/,
                // 兼容部分函数级别
                /^(\.[\w\.]+)\s+0x[0-9a-fA-F]+\s+0x([0-9a-fA-F]+)\s+([\w\-\.]+\.o)\(([^\)]+)\)/
            ];
            for (let line of lines) {
                line = line.trim();
                if (!line || line.startsWith('//')) continue;
                // 汇总行
                if (/^总大小：/i.test(line)) {
                    summary = line;
                    continue;
                }
                let matched = false;
                for (const reg of sectionRegexs) {
                    const m = line.match(reg);
                    if (m) {
                        const section = m[1];
                        const size = parseInt(m[2], 16);
                        let name = m[3];
                        if (m[4]) name += `(${m[4]})`;
                        if (!isNaN(size) && size > 0) {
                            rows.push({ name, section, size });
                        }
                        matched = true;
                        break;
                    }
                }
                if (!matched) {
                    // 兜底：尝试空格分割，最后一列为对象名，前面有section和size
                    const parts = line.split(/\s+/);
                    if (parts.length >= 4 && parts[0].startsWith('.')) {
                        const section = parts[0];
                        const size = parseInt(parts[2], 16);
                        const name = parts[3];
                        if (!isNaN(size) && size > 0) {
                            rows.push({ name, section, size });
                        }
                    }
                }
            }
            return { rows, summary };
        }
        // Bit位分析与转换
        function analyzeBits(valInput) {
            const inputBox = document.getElementById('bitInput');
            const resultDiv = document.getElementById('bitAnalyzeResult');
            const editValue = document.getElementById('bitEditValue');
            let input = typeof valInput === 'string' ? valInput : inputBox.value.trim();
            resultDiv.innerHTML = '';
            editValue.value = '';
            let val = 0;
            let isNegative = false;
            if (!input) {
                val = 0;
            } else if (/^0x[0-9a-fA-F]+$/.test(input)) {
                val = parseInt(input, 16);
            } else if (/^0b[01]+$/.test(input)) {
                val = parseInt(input.slice(2), 2);
            } else if (/^-?\d+$/.test(input)) {
                val = parseInt(input, 10);
                if (val < 0) isNegative = true;
            } else {
                resultDiv.innerHTML = '<span style="color:#e53935;">格式错误，仅支持十进制、0x十六进制、0b二进制</span>';
                return;
            }
            // 32位有符号范围
            if (isNaN(val) || val < -0x80000000 || val > 0xFFFFFFFF) {
                resultDiv.innerHTML = '<span style="color:#e53935;">数值超出范围(-2147483648~0xFFFFFFFF)</span>';
                return;
                       }
            // 负数用补码显示
            let uval = val >>> 0;
            let bits = uval.toString(2).padStart(32, '0');
            let table = '<table style="border-collapse:collapse;font-family:monospace;font-size:14px;min-width:600px;width:100%;table-layout:fixed;">';
            table += '<tr>';
            for (let i = 0; i < 32; i++) {
                let idx = 31 - i;
                table += `<th style=\"padding:1px 2px;border-bottom:1px solid #b2dfdb;color:#888;font-size:12px;font-weight:normal;\">${idx}</th>`;
            }
            table += '</tr>';
            table += '<tr>';
            for (let i = 0; i < 32; i++) {
                let idx = 31 - i;
                table += `<td style=\"padding:1px 2px;\"><span class=\"bit-edit-btn\" data-idx=\"${idx}\" onclick=\"toggleBit(this)\" style=\"display:inline-block;width:18px;text-align:center;cursor:pointer;border:1px solid #ccc;border-radius:2px;background:${bits[i]==='1'?'#e3f2fd':'#fff'};color:${bits[i]==='1'?'#1976d2':'#333'};font-size:15px;\">${bits[i]}</span></td>`;
            }
            table += '</tr>';
            table += '</table>';
            table += '<div style="font-size:12px;color:#888;margin-top:2px;">高位→低位 (bit31 ... bit0)，点击bit位可切换0/1，自动生成对应数值。';
            if ( isNegative) table += ' 当前为负数，显示为32位补码。';
            table += '</div>';
            resultDiv.innerHTML = table;
            updateBitEditValue();
        }
        function toggleBit(el) {
            let v = el.textContent === '1' ? '0' : '1';
            el.textContent = v;
            el.style.background = v === '1' ? '#e3f2fd' : '#fff';
            el.style.color = v === '1' ? '#1976d2' : '#333';
            updateBitEditValue();
        }
        function updateBitEditValue() {
            const btns = document.querySelectorAll('.bit-edit-btn');
            let bits = '';
            btns.forEach(b => bits += b.textContent);
            let val = parseInt(bits, 2);
            let hex = '0x' + val.toString(16).toUpperCase();
            let dec = val.toString(10);
            let bin = '0b' + val.toString(2).padStart(32, '0');
            document.getElementById('bitEditValue').value = `${hex} / ${dec} / ${bin}`;
        }
        function clearBitAnalyze() {
            document.getElementById('bitInput').value = '';
            analyzeBits('');
            document.getElementById('bitEditValue').value = '';
        }
        // 页面加载后自动analyzeBits('')
        window.addEventListener('DOMContentLoaded', function() {
            if (document.getElementById('bit-analyze').classList.contains('active')) {
                analyzeBits('');
            }
        });
        // Hex批量加减
        function hexBatchAddSub() {
            const input = document.getElementById('hexBatchInput').value;
            const op = document.getElementById('hexBatchOp').value;
            const offsetStr = document.getElementById('hexBatchOffset').value.trim();
            const resultBox = document.getElementById('hexBatchResult');
            const errorBox = document.getElementById('hexBatchError');
            errorBox.textContent = '';
            resultBox.value = '';
            // 解析偏移量
            let offset = 0;
            if (/^0x[0-9a-fA-F]{1,2}$/.test(offsetStr)) {
                offset = parseInt(offsetStr, 16);
            } else if (/^\d+$/.test(offsetStr)) {
                offset = parseInt(offsetStr, 10);
            } else {
                errorBox.textContent = '偏移量格式错误，应为0xXX或十进制';
                return;
            }
            // 解析输入
            let hexArr = input.split(/[^0-9a-fA-F]+/).filter(s=>s.length>0);
            // 新增：如果只有一个长串且长度为偶数，自动每2位切分
            if (hexArr.length === 1 && hexArr[0].length > 2 && hexArr[0].length % 2 === 0) {
                let s = hexArr[0];
                hexArr = s.match(/.{2}/g) || [];
            }
            if (hexArr.length === 0) {
                errorBox.textContent = '请输入Hex数据';
                return;
            }
            let outArr = [];
            for (let s of hexArr) {
                if (!/^([0-9a-fA-F]{1,2})$/.test(s)) {
                    errorBox.textContent = `无效Hex: ${s}`;
                    return;
                }
                let v = parseInt(s, 16);
                let res = op === 'add' ? v + offset : v - offset;
                res = ((res % 256) + 256) % 256; // 保证0~255循环
                outArr.push(res.toString(16).toUpperCase().padStart(2, '0'));
            }
            resultBox.value = outArr.join(' ');
        }
        function clearHexBatch() {
            document.getElementById('hexBatchInput').value = '';
            document.getElementById('hexBatchResult').value = '';
            document.getElementById('hexBatchError').textContent = '';
        }
    // 数据曲线拟合工具
    function drawDataFitCurve() {
        let input = document.getElementById('dataFitInput').value;
        const errorBox = document.getElementById('dataFitError');
        const infoBox = document.getElementById('dataFitInfo');
        errorBox.textContent = '';
        infoBox.textContent = '';
        // 提取所有数值
        let numList = input.match(/-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g);
        if (!numList || numList.length < 2) {
            errorBox.textContent = '请输入至少2个有效数值';
            clearDataFitCanvas();
            return;
        }
        let data = numList.map(Number).filter(v=>!isNaN(v));
        let n = data.length;
        // 检查是否所有点都相同
        let allSame = data.every(v => v === data[0]);
        if (allSame) {
            errorBox.textContent = '所有数据点数值完全相同，无法拟合曲线，仅显示水平线';
        }
        // 简单线性拟合 y = a*x + b
        let xArr = Array.from({length:n}, (_,i)=>i+1);
        let sumX = xArr.reduce((a,b)=>a+b,0);
        let sumY = data.reduce((a,b)=>a+b,0);
        let sumXY = xArr.reduce((a,b,i)=>a+b*data[i],0);
        let sumXX = xArr.reduce((a,b)=>a+b*b,0);
        let a = (n*sumXY - sumX*sumY)/(n*sumXX - sumX*sumX);
        let b = (sumY - a*sumX)/n;
        // 拟合曲线点
        let fitY = xArr.map(x=>a*x+b);
        // 绘图
        drawDataFitCanvas(data, fitY, allSame);
        infoBox.textContent = `线性拟合公式: y = ${a.toFixed(4)}*x + ${b.toFixed(4)}`;
    }

    function drawDataFitCanvas(data, fitY, allSame) {
        const canvas = document.getElementById('dataFitCanvas');
        const ctx = canvas.getContext('2d');
        ctx.clearRect(0,0,canvas.width,canvas.height);
        // 坐标轴
        ctx.strokeStyle = '#bbb';
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(40,20); ctx.lineTo(40,300); ctx.lineTo(620,300);
        ctx.stroke();
        // 数据归一化
        let n = data.length;
        let minY = Math.min(...data, ...fitY);
        let maxY = Math.max(...data, ...fitY);
        let yRange = maxY-minY;
        if (yRange === 0) yRange = 1;
        // 纵坐标刻度和数值
        ctx.fillStyle = '#444';
        ctx.font = '13px Arial';
        let tickCount = 6;
        for(let i=0;i<tickCount;i++){
            let y = 300 - (260)*(i/(tickCount-1));
            let value = (minY + (yRange)*(i/(tickCount-1))).toFixed(3);
            ctx.beginPath();
            ctx.moveTo(36,y); ctx.lineTo(40,y);
            ctx.stroke();
            ctx.fillText(value, 2, y+4);
        }
        // 原始点
        ctx.strokeStyle = '#1976d2';
        ctx.lineWidth = 2;
        ctx.beginPath();
        for(let i=0;i<n;i++){
            let x = 40 + (580)*(i)/(n-1);
            let y = 300 - ((data[i]-minY)/yRange)*260;
            if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
        }
        ctx.stroke();
        // 拟合线
        ctx.strokeStyle = '#e53935';
        ctx.lineWidth = 2;
        ctx.beginPath();
        for(let i=0;i<n;i++){
            let x = 40 + (580)*(i)/(n-1);
            let y = 300 - ((fitY[i]-minY)/yRange)*260;
            if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
        }
        ctx.stroke();
        // 标注
        ctx.fillStyle = '#1976d2';
        for(let i=0;i<n;i++){
            let x = 40 + (580)*(i)/(n-1);
            let y = 300 - ((data[i]-minY)/yRange)*260;
            ctx.beginPath();
            ctx.arc(x,y,3,0,2*Math.PI);
            ctx.fill();
        }
    }

    function clearDataFitCurve() {
        document.getElementById('dataFitInput').value = '';
        document.getElementById('dataFitError').textContent = '';
        document.getElementById('dataFitInfo').textContent = '';
        clearDataFitCanvas();
    }

    function clearDataFitCanvas() {
        const canvas = document.getElementById('dataFitCanvas');
        const ctx = canvas.getContext('2d');
        ctx.clearRect(0,0,canvas.width,canvas.height);
    }
    </script>
</body>
</html>